package com.topc.problem.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.topc.base.constant.RedisConstant;
import com.topc.base.exception.Asserts;
import com.topc.base.model.PageParams;
import com.topc.base.model.PageResult;
import com.topc.base.utils.PageUtils;
import com.topc.core.po.BaseEntity;
import com.topc.core.po.ProblemDoCount;
import com.topc.core.sevice.RedisService;
import com.topc.problem.mapper.ProblemInfoMapper;
import com.topc.problem.mapper.ProblemTagMidMapper;
import com.topc.problem.model.dto.SaveProblemDto;
import com.topc.problem.model.dto.QueryProblemInfoDto;
import com.topc.problem.model.enums.DifficultyEnum;
import com.topc.problem.model.po.*;
import com.topc.problem.model.vo.*;
import com.topc.problem.repository.ProblemSampleCollectRepositoryRepository;
import com.topc.problem.service.*;
import com.topc.problem.service.feign.CompetitionClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ProblemInfoServiceImpl extends ServiceImpl<ProblemInfoMapper, ProblemInfo> implements IProblemInfoService {

    @Autowired
    private IProblemTagMidService problemTagMidService;

    @Autowired
    private ProblemTagMidMapper problemTagMidMapper;

    @Autowired
    private RedisService redisService;

    @Autowired
    private ProblemSampleCollectRepositoryRepository problemSampleCollectRepositoryRepository;

    @Autowired
    private IProblemTagService problemTagService;

    @Autowired
    private IProblemComposeInfoService composeInfoService;

    @Autowired
    private CompetitionClient competitionClient;

    @Autowired
    private ProblemInfoMapper problemInfoMapper;

    @Autowired
    private IProblemUserMidService problemUserMidService;



    @Override
    public PageResult<ProblemPageShowVo> searchByPageToAdmin(PageParams pageParams, QueryProblemInfoDto queryProblemInfoDto) {
        queryProblemInfoDto.setSize(queryProblemInfoDto.getTagIds().size());
        Page page = this.getBaseMapper().highSearchPage(pageParams.page(), queryProblemInfoDto);
        PageResult pageResult = PageUtils.pageToResult(page);
        List<ProblemInfo> items = pageResult.getItems();
        List<ProblemPageShowVo> collect = items.stream().map(problemInfo -> {
            ProblemPageShowVo problemPageShowVo = new ProblemPageShowVo(problemInfo);
            ProblemDoCount count = (ProblemDoCount) redisService.hGet(RedisConstant.PROBLEM_DO_COUNT_SUM,
                    problemInfo.getId().toString());
            if(ObjectUtil.isEmpty(count)){
                count = new ProblemDoCount();
                redisService.hSet(RedisConstant.PROBLEM_DO_COUNT_SUM, problemInfo.getId().toString(),count);
            }
            problemPageShowVo.setCommitCount(count.getCommitCount())
                    .setCorrectCount(count.getCorrectCount());
            return problemPageShowVo;
        }).collect(Collectors.toList());
        pageResult.setItems(collect);
        return pageResult;
    }

    @Override
    public Long saveProblem(SaveProblemDto saveProblemDto) {
        ProblemInfo problemInfo = new ProblemInfo();
        BeanUtil.copyProperties(saveProblemDto, problemInfo);
        boolean save = this.save(problemInfo);
        if (!save) {
            Asserts.fail("保存失败");
        }
        redisService.hSet(RedisConstant.PROBLEM_DO_COUNT_SUM,problemInfo.getId().toString(),new ProblemDoCount());
        problemTagMidService.updateProblemTag(problemInfo.getId(), saveProblemDto.getTags());
        return problemInfo.getId();
    }

    @Override
    public List<ProblemSample> getAllTestData(Long problemId) {
        Optional<ProblemSampleCollect> collectOptional = problemSampleCollectRepositoryRepository.findById(String.valueOf(problemId));
        if (collectOptional.isPresent()) {
            return null;
        }
        return collectOptional.get().getSamples();
    }

    @Override
    public List<ProblemPageShowVo> listByIds(List<Long> problemComposeIdList) {
        LambdaQueryWrapper<ProblemInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(ProblemInfo::getId, ProblemInfo::getDifficulty,ProblemInfo::getIsPublic, ProblemInfo::getDifficulty, ProblemInfo::getTitle);
        queryWrapper.in(ProblemInfo::getId, problemComposeIdList);
        List<ProblemInfo> list = this.list(queryWrapper);
        List<ProblemPageShowVo> collect = list.stream().map(problemInfo -> {
            ProblemPageShowVo problemPageShowVo = new ProblemPageShowVo(problemInfo);
            ProblemDoCount count = (ProblemDoCount) redisService.hGet(RedisConstant.PROBLEM_DO_COUNT_SUM,
                    problemInfo.getId().toString());
            if(ObjectUtil.isEmpty(count)){
                count = new ProblemDoCount();
                redisService.hSet(RedisConstant.PROBLEM_DO_COUNT_SUM, problemInfo.getId().toString(),count);
            }
            problemPageShowVo.setCommitCount(count.getCommitCount())
                    .setCorrectCount(count.getCorrectCount());
            return problemPageShowVo;
        }).collect(Collectors.toList());
        return collect;
    }


    @Override
    public Long updateProblemInfo(SaveProblemDto saveProblemDto) {
        ProblemInfo problemInfo = new ProblemInfo();
        BeanUtil.copyProperties(saveProblemDto, problemInfo);
        LambdaUpdateWrapper<ProblemInfo> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(ProblemInfo::getId, problemInfo.getId());
        int update = this.getBaseMapper().update(problemInfo, lambdaUpdateWrapper);
        if (update == 0) {
            Asserts.fail("未查询到此题目");
        }
        problemTagMidService.updateProblemTag(problemInfo.getId(), saveProblemDto.getTags());
        return problemInfo.getId();
    }

    @Override
    public Long updateProblemSamples(Long problemId, List<ProblemSample> problemSample) {
        LambdaQueryWrapper<ProblemInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProblemInfo::getId,problemId);
        int count = this.count(queryWrapper);
        if (count==0) {
            Asserts.fail("题目不存在");
        }
        ProblemSampleCollect problemSampleCollect = new ProblemSampleCollect();
        problemSampleCollect.setSamples(problemSample);
        problemSampleCollect.setId(problemId.toString());
        problemSampleCollectRepositoryRepository.save(problemSampleCollect);
        return problemId;
    }

    @Override
    public ProblemDetailVo getDetailById(Long problemId) {
        ProblemInfo problemInfo = this.getById(problemId);
        if (ObjectUtil.isEmpty(problemInfo)) {
            Asserts.fail("未找到该题目");
        }
        List<ProblemTag> tags = problemTagService.getTags(problemId);
        ProblemDetailVo problemDetailVo = new ProblemDetailVo();
        BeanUtil.copyProperties(problemInfo, problemDetailVo);
        problemDetailVo.setTags(tags.stream().map(BaseEntity::getId).collect(Collectors.toList()));
        problemDetailVo.setTagNames(tags.stream().map(ProblemTag::getName).collect(Collectors.toList()));
        return problemDetailVo;
    }

    /**
     * TODO
     * @param problemId
     * @return
     */
    @Override
    public ProblemDetailShowVo searchDetailById(Long problemId) {
        LambdaQueryWrapper<ProblemInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(ProblemInfo::getId, ProblemInfo::getTitle, ProblemInfo::getIntroduction,ProblemInfo::getTimeLimit,ProblemInfo::getMemoryLimit,ProblemInfo::getInputSample, ProblemInfo::getOutputSample,ProblemInfo::getDifficulty, ProblemInfo::getProblemExplain)
                .eq(ProblemInfo::getId, problemId)
                .eq(ProblemInfo::getIsPublic, 1);
        ProblemInfo problemInfo = this.getOne(queryWrapper);
        if (ObjectUtil.isEmpty(problemInfo)){
            Asserts.fail("未找到该题");
        }
        List<ProblemTagShowVo> problemTagShowVos=problemTagMidMapper.getProblemTagInfo(problemId);
        ProblemDetailShowVo problemDetailShowVo = new ProblemDetailShowVo();
        problemDetailShowVo.setTitle(problemInfo.getTitle());
        problemDetailShowVo.setTagList(problemTagShowVos);
        problemDetailShowVo.setIntroduction(problemInfo.getIntroduction());
        problemDetailShowVo.setInputSample(problemInfo.getInputSample());
        problemDetailShowVo.setOutputSample(problemInfo.getOutputSample());
        problemDetailShowVo.setProblemExplain(problemInfo.getProblemExplain());
        problemDetailShowVo.setTimeLimit(problemInfo.getTimeLimit());
        problemDetailShowVo.setMemoryLimit(problemInfo.getMemoryLimit().divide(BigDecimal.valueOf(1024)));
        problemDetailShowVo.setDifficulty(Objects.requireNonNull(DifficultyEnum.getEnumByValue(problemInfo.getDifficulty())).getText());
        return problemDetailShowVo;
    }

    @Override
    public List<ProblemListShowVo> getProblemList(String userNumber, Long competitionId) {
        //TODO 竞赛状态(禁赛私密或者未公开)(待开发)
        Long composeId=0L;
        List<Long> problemIdList=new ArrayList<>();
        if (competitionId!=0){
            LocalDateTime now = LocalDateTime.now();
            CompetitionCheckInfoVo competitionCheckInfo = competitionClient.getCompetitionCheckInfo(competitionId, userNumber);
            if (ObjectUtil.isEmpty(competitionCheckInfo)||ObjectUtil.isEmpty(competitionCheckInfo.getStartTime())){
                Asserts.fail("竞赛不存在");
            }
            //如果比赛还没结束
            if (competitionCheckInfo.getEndTime().isAfter(now)){
                if (ObjectUtil.isEmpty(competitionCheckInfo.getStatus())){
                    Asserts.fail("未报名");
                }
                if (competitionCheckInfo.getStartTime().isAfter(now)){
                    Asserts.fail("比赛未开始");
                }
            }
            if (ObjectUtil.isNotEmpty(competitionCheckInfo.getStatus())&&competitionCheckInfo.getStatus()==-1){
                Asserts.fail("禁赛中");
            }
            composeId = competitionClient.getComposeIdById(competitionId);
            //获取题单ID
            System.out.println("========================>"+composeId);
            if (ObjectUtil.isEmpty(composeId)){
                Asserts.fail("题单不存在");
            }
            //获取题单内的题目
            LambdaQueryWrapper<ProblemComposeInfo> problemComposeWrapper = new LambdaQueryWrapper<>();
            problemComposeWrapper.select(ProblemComposeInfo::getComposeMid)
                    .eq(ProblemComposeInfo::getId, composeId);
            ProblemComposeInfo problemComposeInfo = composeInfoService.getOne(problemComposeWrapper);
            String composeMid = problemComposeInfo.getComposeMid();
            if (ObjectUtil.isEmpty(composeMid)){
                Asserts.fail("题单为空");
            }
            //将题目ID存为List
            problemIdList=Arrays.stream(composeMid.split(","))
                    .map(Long::parseLong)
                    .collect(Collectors.toList());
        }else {
            LambdaQueryWrapper<ProblemInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.select(ProblemInfo::getId)
                   .eq(ProblemInfo::getIsPublic, 1);
            List<ProblemInfo> list = this.list(queryWrapper);
            for (ProblemInfo info:list){
                problemIdList.add(info.getId());
            }
            if (ObjectUtil.isEmpty(problemIdList)){
                Asserts.fail("题单为空");
            }
        }
        //获取题目信息
        //查询题目id基本信息(ID、名称、提交数、通过数、难度、通过率)
        List<ProblemListShowVo> problemListShowVos=problemInfoMapper.getComposeProblemInfo(problemIdList,userNumber,competitionId);
        if (ObjectUtil.isEmpty(problemListShowVos)){
            Asserts.fail("题目不存在");
        }
        //查询用户提交状态（得分，提交结果）
        for (int i=0;i<problemListShowVos.size(); i++){
            ProblemListShowVo problemListShowVo = problemListShowVos.get(i);
            LambdaQueryWrapper<ProblemUserMid> problemUserMidWrapper = new LambdaQueryWrapper<>();
            problemUserMidWrapper.select(ProblemUserMid::getConditionMid,ProblemUserMid::getScore)
                    .eq(ProblemUserMid::getProblemId,problemListShowVo.getProblemId())
                    .eq(ProblemUserMid::getUserNumber,userNumber)
                    .eq(ProblemUserMid::getCompetitionId,competitionId)
                    .orderByDesc(ProblemUserMid::getScore,ProblemUserMid::getUpdateTime)
                    .last("limit 1");
            ProblemUserMid problemUserMid = problemUserMidService.getOne(problemUserMidWrapper);
            if (ObjectUtil.isEmpty(problemUserMid)){
                problemListShowVo.setResult("");
                problemListShowVo.setScore(0);
            }else{
                JudgeInfo judgeInfo = JSONUtil.toBean(problemUserMid.getConditionMid(), JudgeInfo.class);
                problemListShowVo.setResult(judgeInfo.getMessage());
                problemListShowVo.setScore(problemUserMid.getScore());
            }
        }
        System.out.println(problemListShowVos);
        //查询对应题目ID以及竞赛ID的总提交量以及通过量
        return problemListShowVos;
    }

}
